/*
 * px-hmac.c
 *		HMAC 实现。
 *
 * Copyright (c) 2001 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他材料提供的发行版本中。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, 或利润; 或业务中断)
 * HOWEVER CAUSED AND ON ANY理论的责任，不论是合同、严格责任还是侵权（包括疏忽或其他）以任何方式引起的
 * 使用本软件，即使在被告知可能性时也不承担任何责任。
 *
 * contrib/pgcrypto/px-hmac.c
 */

#include "postgres.h"

#include "px.h"

#define HMAC_IPAD 0x36
#define HMAC_OPAD 0x5C

static unsigned fc_hmac_result_size(PX_HMAC *fc_h)
{
	return px_md_result_size(fc_h->md);
}

static unsigned fc_hmac_block_size(PX_HMAC *fc_h)
{
	return px_md_block_size(fc_h->md);
}

static void fc_hmac_init(PX_HMAC *fc_h, const uint8 *fc_key, unsigned fc_klen)
{
	unsigned	fc_bs,
				fc_i;
	uint8	   *fc_keybuf;
	PX_MD	   *fc_md = fc_h->md;

	fc_bs = px_md_block_size(fc_md);
	fc_keybuf = palloc0(fc_bs);

	if (fc_klen > fc_bs)
	{
		px_md_update(fc_md, fc_key, fc_klen);
		px_md_finish(fc_md, fc_keybuf);
		px_md_reset(fc_md);
	}
	else
		memcpy(fc_keybuf, fc_key, fc_klen);

	for (fc_i = 0; fc_i < fc_bs; fc_i++)
	{
		fc_h->p.ipad[fc_i] = fc_keybuf[fc_i] ^ HMAC_IPAD;
		fc_h->p.opad[fc_i] = fc_keybuf[fc_i] ^ HMAC_OPAD;
	}

	px_memset(fc_keybuf, 0, fc_bs);
	pfree(fc_keybuf);

	px_md_update(fc_md, fc_h->p.ipad, fc_bs);
}

static void fc_hmac_reset(PX_HMAC *fc_h)
{
	PX_MD	   *fc_md = fc_h->md;
	unsigned	fc_bs = px_md_block_size(fc_md);

	px_md_reset(fc_md);
	px_md_update(fc_md, fc_h->p.ipad, fc_bs);
}

static void fc_hmac_update(PX_HMAC *fc_h, const uint8 *fc_data, unsigned fc_dlen)
{
	px_md_update(fc_h->md, fc_data, fc_dlen);
}

static void fc_hmac_finish(PX_HMAC *fc_h, uint8 *fc_dst)
{
	PX_MD	   *fc_md = fc_h->md;
	unsigned	fc_bs,
				fc_hlen;
	uint8	   *fc_buf;

	fc_bs = px_md_block_size(fc_md);
	fc_hlen = px_md_result_size(fc_md);

	fc_buf = palloc(fc_hlen);

	px_md_finish(fc_md, fc_buf);

	px_md_reset(fc_md);
	px_md_update(fc_md, fc_h->p.opad, fc_bs);
	px_md_update(fc_md, fc_buf, fc_hlen);
	px_md_finish(fc_md, fc_dst);

	px_memset(fc_buf, 0, fc_hlen);
	pfree(fc_buf);
}

static void fc_hmac_free(PX_HMAC *fc_h)
{
	unsigned	fc_bs;

	fc_bs = px_md_block_size(fc_h->md);
	px_md_free(fc_h->md);

	px_memset(fc_h->p.ipad, 0, fc_bs);
	px_memset(fc_h->p.opad, 0, fc_bs);
	pfree(fc_h->p.ipad);
	pfree(fc_h->p.opad);
	pfree(fc_h);
}


/* 公共函数 */

int px_find_hmac(const char *fc_name, PX_HMAC **fc_res)
{
	int			fc_err;
	PX_MD	   *fc_md;
	PX_HMAC    *fc_h;
	unsigned	fc_bs;

	fc_err = px_find_digest(fc_name, &fc_md);
	if (fc_err)
		return fc_err;

	fc_bs = px_md_block_size(fc_md);
	if (fc_bs < 2)
	{
		px_md_free(fc_md);
		return PXE_HASH_UNUSABLE_FOR_HMAC;
	}

	fc_h = palloc(sizeof(*fc_h));
	fc_h->p.ipad = palloc(fc_bs);
	fc_h->p.opad = palloc(fc_bs);
	fc_h->md = fc_md;

	fc_h->result_size = fc_hmac_result_size;
	fc_h->block_size = fc_hmac_block_size;
	fc_h->reset = fc_hmac_reset;
	fc_h->update = fc_hmac_update;
	fc_h->finish = fc_hmac_finish;
	fc_h->free = fc_hmac_free;
	fc_h->init = fc_hmac_init;

	*fc_res = fc_h;

	return 0;
}
